package com.hitqz.robot.biz.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.business.dto.*;
import com.hitqz.robot.api.business.entity.*;
import com.hitqz.robot.api.dispatch.protocol.dto.ReportLogMessageDto;
import com.hitqz.robot.api.dispatch.protocol.dto.UploadMediaUrlDto;
import com.hitqz.robot.biz.service.*;
import com.hitqz.robot.common.core.util.RedisUtils;
import com.hitqz.robot.common.file.core.FileProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.*;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author xupkun
 * @date 2024/7/31
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class RobotOtherEventListener {


    private final RobotService robotService;

    private final RobotGroupService robotGroupService;

    private final RobotWarningRecordService robotWarningRecordService;

    private final PatrolResultService patrolResultService;

    private final SentinelModeRecordService sentinelModeRecordService;

//    private final InfluxDBService influxDBService;


    private final FileProperties fileProperties;

    private static String zhPattern = "[\\u4e00-\\u9fa5]";

    public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10,20,
            1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(20),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());

    @EventListener
    public void listenerRobotOnline(RobotAddDispatchDto robotAddDispatchDto) {
        log.info("有机器人状态修改: {}", robotAddDispatchDto.getRobotSn());

        // 清空缓存，避免使用过时数据
        RedisUtils.del("robot::client");

        // 获取最新机器人数据
        RobotEntity latestRobotDb = robotService.selectByRobotSn(robotAddDispatchDto.getRobotSn());
        if (latestRobotDb == null) return;

        // 检查当前组内是否存在地图不一致的机器人
        List<RobotEntity> nowGroupRobotList = robotService.list(
                Wrappers.<RobotEntity>lambdaQuery().eq(RobotEntity::getGroupId, latestRobotDb.getGroupId())
        );

        for (RobotEntity entity : CollUtil.emptyIfNull(nowGroupRobotList)) {
            if (!Objects.equals(entity.getId(), latestRobotDb.getId())
                    && !Objects.equals(entity.getMapCode(), latestRobotDb.getMapCode())) {

                String content = latestRobotDb.getRobotName() + "所处调度组有机器人存在地图不一致，无法进入调度范围";
                log.warn("地图不一致: {}", content);

                AddWarningRecordDto warningRecordDto = new AddWarningRecordDto();
                warningRecordDto.setLevel(1);
                warningRecordDto.setContent(content);
                SpringUtil.getApplicationContext().publishEvent(warningRecordDto);
                return;
            }
        }

        // 地图一致，进入缓存更新逻辑
        threadPool.execute(() -> updateRobotDispatchCache(latestRobotDb, robotAddDispatchDto.getOldGroupId()));
    }

    private void updateRobotDispatchCache(RobotEntity robot, Integer oldGroupId) {
        Cache<Integer, List<RobotEntity>> robotDispatch = SpringUtil.getBean("robotDispatch");

        synchronized (String.valueOf(robot.getGroupId()).intern()) {
            // 移除旧组中的机器人
            if (oldGroupId != null) {
                List<RobotEntity> oldGroup = robotDispatch.getIfPresent(oldGroupId);
                if (CollectionUtil.isNotEmpty(oldGroup)) {
                    oldGroup.removeIf(e -> Objects.equals(e.getId(), robot.getId()));
                    robotDispatch.put(oldGroupId, oldGroup);
                }
            }

            // 添加到新组缓存中（先去重再添加）
            List<RobotEntity> newGroup = robotDispatch.getIfPresent(robot.getGroupId());
            if (newGroup == null) newGroup = new ArrayList<>();
            newGroup.removeIf(e -> Objects.equals(e.getId(), robot.getId()));
            newGroup.add(robot);
            robotDispatch.put(robot.getGroupId(), newGroup);

            RobotGroupEntity robotGroupEntity = robotGroupService.getById(robot.getGroupId());
            log.info("{} 开始进入写入调度组计算 {}", robotGroupEntity.getName(), robot.getRobotName());
        }
    }

    @EventListener
    public void listenerWarning(AddWarningRecordDto addWarningRecordDto) {
        RobotWarningRecordEntity robotWarningRecord = new RobotWarningRecordEntity();
        robotWarningRecord.setFlag("0");
        robotWarningRecord.setContent(addWarningRecordDto.getContent());
        robotWarningRecord.setServiceId("系统告警");
        robotWarningRecord.setTime(LocalDateTime.now());
        robotWarningRecord.setTitle("系统告警");
        robotWarningRecord.setType("1");
        robotWarningRecord.setLevel(addWarningRecordDto.getLevel());
        robotWarningRecordService.save(robotWarningRecord);
    }


    @EventListener
    public void listenerSentinelModeRecord(SentinelModeRecordDto dto){
        String basePath = fileProperties.getLocal().getBasePath();
        log.info("收到哨兵模式记录:{}", dto);
        SentinelModeRecordEntity entity = BeanUtil.copyProperties(dto, SentinelModeRecordEntity.class);
        String picUrl = entity.getUrl();
        if (StrUtil.isNotBlank(picUrl)) {
            //查询最新100条记录数
            List<SentinelModeRecordEntity> list = sentinelModeRecordService.list(Wrappers.<SentinelModeRecordEntity>lambdaQuery().eq(SentinelModeRecordEntity::getRobotSn, entity.getRobotSn()).orderByDesc(SentinelModeRecordEntity::getId).last("limit 100"));
            if (list.size() == 100){
                SentinelModeRecordEntity oldRecord = list.get(99);
                sentinelModeRecordService.remove(Wrappers.<SentinelModeRecordEntity>lambdaQuery().eq(SentinelModeRecordEntity::getRobotSn, entity.getRobotSn()).lt(SentinelModeRecordEntity::getId, oldRecord.getId()));
                deleteOldFiles(new File(basePath + File.separator + "sentinel" + File.separator + entity.getRobotSn()),
                        oldRecord.getTime().getTime());
            }
            //取 mediaUrl的文件后缀
            String extName = FileUtil.extName(picUrl);
            String filePath = basePath + File.separator + "sentinel"
                    + File.separator + entity.getRobotSn()
                    + File.separator + DateUtil.format(new Date(), "yyyyMMdd")
                    + File.separator + DateUtil.format(new Date(), "HHmmssSSS") + "." + extName;
            File file = new File(filePath);
            if (!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            String relativePath = normalizePath(filePath).replace(normalizePath(basePath), "/sys-file");
            URL url = URLUtil.url(picUrl);
            FileUtil.writeFromStream(URLUtil.getStream(url), filePath);
            entity.setUrl(relativePath);
            //保存
            sentinelModeRecordService.save(entity);
        }
    }

    private void deleteOldFiles(File folder, long cutoffTime) {
        File[] files = folder.listFiles();
        if (files == null) return;
        for (File file : files) {
            if (file.isDirectory()) {
                deleteOldFiles(file, cutoffTime); // 递归处理子文件夹
                if (FileUtil.isDirEmpty(file)) { // 删除空文件夹
                    FileUtil.del(file);
                }
            } else {
                if (file.lastModified() < cutoffTime) { // 判断文件修改时间
                    FileUtil.del(file);
                }
            }
        }
    }



    @EventListener
    public void listenerPatrolResult(PatrolResultDto patrolResultDto) {
        log.info("收到检测记录:{}", patrolResultDto);
        PatrolResultEntity patrolResultEntity = new PatrolResultEntity();
        BeanUtil.copyProperties(patrolResultDto, patrolResultEntity);
        //避免覆盖
        boolean InvalidationFlag = false;
        String specialCause = "";
        if (StrUtil.isNotBlank(patrolResultDto.getAuditRemark())){
            InvalidationFlag = true;
            specialCause = patrolResultDto.getAuditRemark();
        }
        patrolResultEntity.setUploadStatus(null);
        patrolResultEntity.setAuditRemark(null);
        patrolResultEntity.setAuditStatus(null);
        patrolResultEntity.setAuditor(null);
        patrolResultEntity.setOperator(null);
        patrolResultEntity.setCause(null);
        patrolResultEntity.setAuditTime(null);
        PatrolResultEntity dbPatrolResult = patrolResultService.getOne(Wrappers.<PatrolResultEntity>lambdaQuery()
                .eq(PatrolResultEntity::getTicketsId, patrolResultDto.getTicketsId())
                .eq(PatrolResultEntity::getItemId, patrolResultDto.getItemId())
                .last("limit 1"));
        if (dbPatrolResult!=null) {
            patrolResultEntity.setId(dbPatrolResult.getId());
        }else{
            patrolResultEntity.setId(null);
        }
        try {
            String basePath = normalizePath(fileProperties.getLocal().getBasePath());

            if (StrUtil.isNotBlank(patrolResultDto.getMediaUrl())) {
                //取 mediaUrl的文件后缀
                String extName = FileUtil.extName(patrolResultDto.getMediaUrl());
                String filePath = fileProperties.getLocal().getBasePath() + File.separator + "patrol"
                        + File.separator + patrolResultDto.getClientId()
                        + File.separator + DateUtil.format(new Date(), "yyyy-MM-dd")
                        + File.separator + patrolResultDto.getId() + "_N_" + patrolResultDto.getItemName() + "." + extName;
                String relativePath = normalizePath(filePath).replace(normalizePath(basePath), "/sys-file");

                URL url = URLUtil.url(patrolResultDto.getMediaUrl());
                FileUtil.writeFromStream(URLUtil.getStream(url), filePath);
                patrolResultEntity.setMediaUrl(relativePath);
            } else {
                patrolResultEntity.setMediaUrl(null);
            }

            if (StrUtil.isNotBlank(patrolResultDto.getVisionMediaUrl())) {
                String extName = FileUtil.extName(patrolResultDto.getVisionMediaUrl());
                String vFilePath = fileProperties.getLocal().getBasePath() + File.separator + "patrol"
                        + File.separator + patrolResultDto.getClientId()
                        + File.separator + DateUtil.format(new Date(), "yyyy-MM-dd")
                        + File.separator + patrolResultDto.getId() + "_V_" + patrolResultDto.getItemName() + "." + extName;
                String vRelativePath = normalizePath(vFilePath).replace(normalizePath(basePath), "/sys-file");

                URL vUrl = URLUtil.url(patrolResultDto.getVisionMediaUrl());
                File file = FileUtil.writeFromStream(URLUtil.getStream(vUrl), vFilePath);
                if (file.exists() && patrolResultDto.getItemName().contains("音")){
                    log.info("切割噪音图片");
                    var path = fileProperties.getLocal().getBasePath() + File.separator + "home" + File.separator + patrolResultDto.getClientId() + File.separator + "voice";
                    ImgUtil.cut(file, new File(path+File.separator+"1.jpg"), new Rectangle(0, 0, 700, 300));
                    ImgUtil.cut(file, new File(path+File.separator+"2.jpg"), new Rectangle(0, 300, 700, 300));
                }
                patrolResultEntity.setVisionMediaUrl(vRelativePath);
                RedisUtils.set("patrol:visionMediaUrl:" + patrolResultDto.getClientId()  +":"+ patrolResultDto.getItemId(),patrolResultDto.getVisionMediaUrl());
            } else {
                patrolResultEntity.setVisionMediaUrl(null);
            }
        }catch (Exception e){
            log.error("patrol result download error");
        }
        if (patrolResultEntity.getId()==null){
            patrolResultService.save(patrolResultEntity);
        }else {
            //重复报警情况
            String key = "patrol:alarmDisable:hours" + patrolResultDto.getClientId() + ":" + patrolResultDto.getItemId();
            String key2 = "patrol:alarmDisable:tips" + patrolResultDto.getClientId() + ":" + patrolResultDto.getItemId();
            Integer hours = RedisUtils.get(key);
            String tips = RedisUtils.get(key2);
            //视觉检测异常的项目且设置了不重复报警
            if (Objects.equals(patrolResultEntity.getStatus(),2) && StrUtil.isNotBlank(tips) && hours!=null){
                // 如果没有特殊情况（例如滴漏面积变大）则改成正常
                if (!InvalidationFlag){
                    patrolResultEntity.setStatus(1);
                    patrolResultEntity.setAuditRemark(tips);
                }else {
                    log.info("特殊情况:{},重复报警过滤功能该次失效",specialCause);
                }
            }
            // 先做一次 updateById，更新非空字段
            patrolResultService.updateById(patrolResultEntity);
            // 如果 url 字段是 null，主动 set 数据库字段为 null
            UpdateWrapper<PatrolResultEntity> nullUpdateWrapper = new UpdateWrapper<>();
            nullUpdateWrapper.eq("id", patrolResultEntity.getId());
            boolean needPatch = false;
            if (patrolResultEntity.getMediaUrl() == null) {
                nullUpdateWrapper.set("media_url", null);
                needPatch = true;
            }
            if (patrolResultEntity.getVisionMediaUrl() == null) {
                nullUpdateWrapper.set("vision_media_url", null);
                needPatch = true;
            }
            if (needPatch) {
                patrolResultService.update(null, nullUpdateWrapper);
            }
        }
    }

    @EventListener
    public void onUploadMediaUrl(UploadMediaUrlDto dto){
        RedisUtils.set("mediaUpload:"+dto.getClientId(), JSONObject.toJSONString(dto),5);
    }


    public static String encode(String str, String charset) throws UnsupportedEncodingException {
        Pattern p = Pattern.compile(zhPattern);
        Matcher m = p.matcher(str);
        StringBuffer b = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(b, URLEncoder.encode(m.group(0), charset));
        }
        m.appendTail(b);
        return b.toString();
    }


    // 工具方法：将路径统一转为 "/" 分隔（推荐放到工具类）
    private String normalizePath(String path) {
        return path.replace("\\", "/");
    }


//    @EventListener
//    @Async
//    public void saveSensorData(RobotClientSummaryDto dto){
//        try{
//            Optional<RobotCustomInfoDto> h2 = Optional.of(dto)
//                    .map(RobotClientSummaryDto::getOtherInfo)
//                    .map(RobotOtherInfoDto::getCustomInfos)
//                    .flatMap(f -> f.stream().filter(f1 -> Objects.equals(f1.getLabel(), "氢气")).findFirst());
//            h2.ifPresent(robotCustomInfoDto -> influxDBService.writeData(dto.getClientId(), robotCustomInfoDto.getKey(), Double.parseDouble(robotCustomInfoDto.getValue())));
//        }catch( Exception e){
//
//        }
//    }

}
